# +----------------------------------------------------------------------
# | ChatWork智能聊天办公系统
# +----------------------------------------------------------------------
# | 软件声明: 本系统并非自由软件,未经授权任何形式的商业使用均属非法。
# | 版权保护: 任何企业和个人不允许对程序代码以任何形式任何目的复制/分发。
# | 授权要求: 如有商业使用需求,请务必先与版权所有者取得联系并获得正式授权。
# +----------------------------------------------------------------------
# | Author: ChatWork Team <2474369941@qq.com>
# +----------------------------------------------------------------------
import json
import time
from typing import List
from hypertext import PagingResult
from exception import AppException
from common.utils.urls import UrlUtil
from common.utils.tools import ToolsUtil
from common.enums.models import ModelsEnum
from common.models.dev import DevModelsModel
from common.models.docs import DocsPromptModel
from apps.admin.schemas.apply.docs import prompt_schema as schema


class DocsPromptService:

    @classmethod
    async def lists(cls, params: schema.DocsPromptSearchIn) -> PagingResult[schema.DocsPromptListVo]:
        """
        模式列表。

        Args:
            params (schema.DocsPromptSearchIn): 查询参数。

        Returns:
            PagingResult[schema.DocsPromptListVo]: 分页列表Vo。

        Author:
            zero
        """
        # 查询条件
        where = DocsPromptModel.build_search({
            "%like%": ["name"]
        }, params.__dict__)

        # 查询数据
        _model = DocsPromptModel.filter(*where).order_by("-sort", "-id")
        _pager = await DocsPromptModel.paginate(
            model=_model,
            page_no=params.page_no,
            page_size=params.page_size,
            fields=["id", "code", "icon", "name", "intro", "sort", "is_disable", "is_builtin", "create_time"]
        )

        # 处理数据
        _lists = []
        for item in _pager.lists:
            _lists.append(schema.DocsPromptListVo(
                id=item["id"],
                code=item["code"],
                icon=await UrlUtil.to_absolute_url(item["icon"]),
                name=item["name"],
                intro=item["intro"],
                sort=item["sort"],
                is_builtin=item["is_builtin"],
                is_disable=item["is_disable"],
                create_time=item["create_time"]
            ))

        _pager.lists = _lists
        return _pager

    @classmethod
    async def detail(cls, id_: int) -> schema.DocsPromptDetailVo:
        """
        模式详情。

        Args:
            id_ (int): 查询参数。

        Returns:
            PagingResult[schema.DocsPromptDetailVo]: 分页列表Vo。

        Author:
            zero
        """
        detail = await DocsPromptModel.filter(id=id_).get()

        dialogue = json.loads(detail.models_dialogue or "{}")
        abstract = json.loads(detail.models_abstract or "{}")
        translate = json.loads(detail.models_translate or "{}")
        c_dialogue = cls._get_config_with_defaults(dialogue)
        c_abstract = cls._get_config_with_defaults(abstract)
        c_translate = cls._get_config_with_defaults(translate)

        return schema.DocsPromptDetailVo(
            id=detail.id,
            code=detail.code,
            icon=await UrlUtil.to_absolute_url(detail.icon),
            name=detail.name,
            intro=detail.intro,
            sort=detail.sort,
            is_disable=detail.is_disable,
            prompt_dialogue=detail.prompt_dialogue or "",
            prompt_abstract=detail.prompt_abstract or "",
            prompt_translate=detail.prompt_translate or "",
            models_dialogue=dialogue.get("model", ""),
            models_abstract=abstract.get("model", ""),
            models_translate=translate.get("model", ""),
            config_dialogue=c_dialogue,
            config_abstract=c_abstract,
            config_translate=c_translate,
        )

    @classmethod
    async def add(cls, post: schema.DocsPromptAddIn):
        """
        模式添加。

        Args:
            post (schema.DocsPromptAddIn): 添加参数。

        Author:
            zero
        """
        # 初始模型键值
        params = post.__dict__
        modelDicts = {"dialogue": {}, "abstract": {}, "translate": {}}
        modelNames = {"dialogue": "提问模型", "abstract": "总结模型", "translate": "翻译模型"}
        modelLists = {
            "dialogue": post.models_dialogue,
            "abstract": post.models_abstract,
            "translate": post.models_translate
        }

        # 模型默认参数
        for key, value in modelLists.items():
            modelConf: dict = params[str(f"config_{key}")]
            modelArr: List[str] = value.split(":")
            dbModels = await (DevModelsModel
                              .filter(scene=ModelsEnum.TYPE_CHAT)
                              .filter(is_disable=0, is_delete=0)
                              .filter(mark=modelArr[0])
                              .order_by("sort", "-id")
                              .first())

            if dbModels and len(modelArr) >= 2:
                config = json.loads(dbModels.config)
                models = json.loads(dbModels.models)
                config.pop("check_key", None)
                config.pop("agency_api", None)
                config.pop("global_directives", None)
                if key != "dialogue":
                    config.pop("context_num", None)
                for k, v in config.items():
                    config[k] = modelConf.get(k, config[k])
                for llm in models:
                    if llm["model"] == modelArr[1]:
                        if not llm["status"]:
                            raise AppException(f"{modelNames[key]}: 已下架")
                        m = {"model": value, "channel": str(dbModels.channel)}
                        m.update(config)
                        modelDicts[key] = m
            else:
                raise AppException(f"{modelNames[key]}: 不存在或已下架")

        code: str = await ToolsUtil.make_rand_sn(DocsPromptModel, "code", 8)
        await DocsPromptModel.create(
            code=code,
            icon=UrlUtil.to_relative_url(post.icon),
            name=post.name,
            intro=post.intro,
            sort=post.sort,
            is_disable=post.is_disable,
            create_time=int(time.time()),
            update_time=int(time.time()),
            prompt_dialogue=post.prompt_dialogue,
            prompt_abstract=post.prompt_abstract,
            prompt_translate=post.prompt_translate,
            models_dialogue=json.dumps(modelDicts["dialogue"], ensure_ascii=False),
            models_abstract=json.dumps(modelDicts["abstract"], ensure_ascii=False),
            models_translate=json.dumps(modelDicts["translate"], ensure_ascii=False)
        )

    @classmethod
    async def edit(cls, post: schema.DocsPromptEditIn):
        """
        模式编辑。

        Args:
            post (schema.DocsPromptEditIn): 编辑参数。

        Author:
            zero
        """
        # 初始模型键值
        params = post.__dict__

        modelDicts = {"dialogue": {}, "abstract": {}, "translate": {}}
        modelNames = {"dialogue": "提问模型", "abstract": "总结模型", "translate": "翻译模型"}
        modelLists = {
            "dialogue": str(post.models_dialogue),
            "abstract": post.models_abstract,
            "translate": post.models_translate
        }

        # 模型默认参数
        for key, value in modelLists.items():
            modelConf: dict = params[str(f"config_{key}")]
            modelArr: List[str] = value.split(":")
            dbModels = await (DevModelsModel
                              .filter(scene=ModelsEnum.TYPE_CHAT)
                              .filter(is_disable=0)
                              .filter(is_delete=0)
                              .filter(mark=modelArr[0])
                              .order_by("sort", "-id")
                              .first())

            if dbModels and len(modelArr) >= 2:
                config = json.loads(dbModels.config)
                models = json.loads(dbModels.models)
                config.pop("agency_api", None)
                config.pop("check_key", None)
                config.pop("global_directives", None)
                if key != "dialogue":
                    config.pop("context_num", None)
                for k, v in config.items():
                    config[k] = modelConf.get(k, config[k])
                for llm in models:
                    if llm["model"] == modelArr[1]:
                        if not llm["status"]:
                            raise AppException(f"{modelNames[key]}: 已下架")
                        m = {"model": value, "channel": dbModels.channel}
                        m.update(config)
                        modelDicts[key] = m
                if not modelDicts.get(key):
                    raise AppException(f"{modelNames[key]}: 不存在或已下架")
            else:
                raise AppException(f"{modelNames[key]}: 不存在或已下架")

        await DocsPromptModel.filter(id=post.id).update(
            icon=UrlUtil.to_relative_url(post.icon),
            name=post.name,
            intro=post.intro,
            sort=post.sort,
            is_disable=post.is_disable,
            update_time=int(time.time()),
            prompt_dialogue=post.prompt_dialogue,
            prompt_abstract=post.prompt_abstract,
            prompt_translate=post.prompt_translate,
            models_dialogue=json.dumps(modelDicts["dialogue"], ensure_ascii=False),
            models_abstract=json.dumps(modelDicts["abstract"], ensure_ascii=False),
            models_translate=json.dumps(modelDicts["translate"], ensure_ascii=False)
        )

    @classmethod
    async def delete(cls, id_: int):
        """
        模式删除。

        Args:
            id_ (int): 模式id。

        Author:
            zero
        """
        await DocsPromptModel.filter(id=id_, is_delete=0).get()
        await DocsPromptModel.filter(id=id_).update(
            is_delete=1,
            delete_time=int(time.time())
        )

    @classmethod
    def _get_config_with_defaults(cls, data: dict):
        """
        获取默认参数。

        Args:
            data (dict): 模型参数。

        Author:
            zero
        """
        configs_list = []
        model = ModelsEnum.chat_models().get(data.get("channel")) or {}
        if model:
            configs = model.get("configs", [])[0].get("config", [])
            for config in configs:
                if config.get("key") not in ["context_num", "agency_api", "global_directives"]:
                    if data.get(config.get("key")) is not None:
                        config["default"] = data.get(config.get("key"))
                    configs_list.append(config)
        return configs_list
